package com.gitee.dupe.common.captcha;

import cn.hutool.captcha.AbstractCaptcha;
import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.generator.CodeGenerator;
import cn.hutool.captcha.generator.MathGenerator;
import cn.hutool.captcha.generator.RandomGenerator;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.StrUtil;
import com.gitee.dolt.cache.repository.CacheOps;
import com.gitee.dolt.core.exception.BizException;
import com.gitee.dolt.core.exception.code.ExceptionCode;
import com.gitee.dupe.common.cache.CaptchaCacheKeyBuilder;
import com.gitee.dupe.common.properties.CaptchaShape;
import com.gitee.dupe.common.properties.CaptchaType;
import com.gitee.dupe.common.properties.DupeProperties;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Objects;

/**
 * @author LYH
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class CaptchaService {

    private final CaptchaCacheKeyBuilder captchaCacheKeyBuilder = new CaptchaCacheKeyBuilder();

    private final CacheOps cacheOps;

    private final DupeProperties dupeProperties;

    private CodeGenerator codeGenerator;


    public void renderCaptcha(String key, HttpServletResponse response) {
        int width = 115;
        int height = 42;
        DupeProperties.CaptchaProperties captchaProperties = dupeProperties.getCaptcha();
        CaptchaShape shape = captchaProperties.getShape();
        AbstractCaptcha captcha;
        if (Objects.equals(shape, CaptchaShape.TRANSPARENT)) {
            captcha = new TransparentCaptcha(width, height, 0);
        } else if (Objects.equals(shape, CaptchaShape.CIRCLE)) {
            captcha = CaptchaUtil.createCircleCaptcha(width, height, 0, 5);
        } else if (Objects.equals(shape, CaptchaShape.SHEAR)) {
            captcha = CaptchaUtil.createLineCaptcha(width, height, 0, 5);
        } else {
            captcha = CaptchaUtil.createLineCaptcha(width, height, 0, 5);
        }
        captcha.setGenerator(getCodeGenerator());
        cacheOps.set(captchaCacheKeyBuilder.key(key), captcha.getCode());
        try {
            captcha.write(response.getOutputStream());
        } catch (IOException e) {
            log.error("验证码渲染失败");
        }
    }

    public void checkCaptcha(String key, String inputCode) {
        DupeProperties.CaptchaProperties captchaProperties = dupeProperties.getCaptcha();
        if (BooleanUtil.isTrue(captchaProperties.getEnable())) {
            if (StrUtil.hasEmpty(key, inputCode)) {
                throw new BizException(ExceptionCode.CAPTCHA_ERROR);
            }
            String code = cacheOps.get(captchaCacheKeyBuilder.key(key), false);
            if (StrUtil.isEmpty(code) || !getCodeGenerator().verify(code, inputCode)) {
                // 验证码错误
                throw new BizException(ExceptionCode.CAPTCHA_ERROR);
            }
        }
    }

    public void checkCaptchaAndRemove(String key, String inputCode) {
        checkCaptcha(key, inputCode);
        removeCaptchaKey(key);
    }

    public void removeCaptchaKey(String key) {
        cacheOps.del(captchaCacheKeyBuilder.key(key));
    }

    private CodeGenerator getCodeGenerator() {
        if (this.codeGenerator == null) {
            synchronized (this) {
                if (this.codeGenerator == null) {
                    if (Objects.equals(this.dupeProperties.getCaptcha().getType(), CaptchaType.MATH)) {
                        this.codeGenerator = new MathGenerator(1);
                    } else {
                        this.codeGenerator = new RandomGenerator("0123456789", 4);
                    }
                }
            }
        }
        return this.codeGenerator;
    }
}
